package basedeal

import (
	"context"
	"core/datetime"
	mymongo "core/mongo"
	"fmt"
	"iotall/iotcore/entity"
	"strconv"

	"github.com/todocoder/go-stream/stream"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

func GetDeviceCollection() *mongo.Collection {
	var col = mymongo.Db.Collection("StdPkgDevice")
	return col
}
func LoadStdPkgDevices(m *entity.StdPkg) (*[]entity.StdPkgDeviceMongo, error) {
	var r []entity.StdPkgDeviceMongo

	var ids = make([]string, 0)
	for _, v := range m.Devices {
		var id = fmt.Sprintf("%v*%v", m.Sid, v.Id)
		ids = append(ids, id)
	}

	var col = mymongo.Db.Collection("StdPkgDevice")
	filter := bson.M{"_id": bson.M{"$in": ids}}

	var curr, err = col.Find(context.TODO(), filter)
	if err != nil {
		return nil, err
	}
	defer curr.Close(context.TODO())

	for curr.Next(context.TODO()) {
		var m = &entity.StdPkgDeviceMongo{}
		err = curr.Decode(m)
		if err != nil {
			return nil, err
		}
		r = append(r, *m)
	}
	return &r, nil
}
func GetOldDev(l *[]entity.StdPkgDeviceMongo, id string) *entity.StdPkgDeviceMongo {
	for _, v := range *l {
		if v.Id == id {
			return &v
		}
	}
	return nil
}
func GetDId(sid, did string) string {
	return fmt.Sprintf("%v*%v", sid, did)
}
func GetPId(sid, did, pid string) string {
	return fmt.Sprintf("%v*%v*%v", sid, did, pid)
}

// SaveDevice 保存设备，设备或设备属性可能有增加
func SaveDevice(m *entity.StdPkg) (*[]entity.StdPkgDeviceMongo, error) {

	var lold, err = LoadStdPkgDevices(m)
	if err != nil {
		return nil, err
	}
	for _, v := range m.Devices {
		var did = GetDId(m.Sid, v.Id)
		var molddev = GetOldDev(lold, did)
		if molddev == nil {
			err = InsertDevice(m.Sid, &v, lold)
			if err != nil {
				return nil, err
			}
		} else {
			err = UpdateDevice(m.Sid, &v, molddev)
			if err != nil {
				return nil, err
			}
		}
	}

	return lold, nil
}
func InsertDevice(sid string, m *entity.StdPkgDevice, l *[]entity.StdPkgDeviceMongo) error {
	var mdev = entity.StdPkgDeviceMongo{
		Id:          fmt.Sprintf("%v*%v", sid, m.Id),
		Sid:         sid,
		Did:         m.Id,
		Name:        m.Name,
		Ability:     m.Ability,
		Props:       make([]entity.PropDefine, 0),
		CrtDt:       datetime.NowStr(),
		PropCustoms: make([]entity.PropCustom, 0),
		PropValues:  make([]entity.PropValue, 0),
	}
	var sm = stream.Of(m.Props...).Sorted(func(a, b entity.StdPkgProp) bool {
		return a.Id < b.Id
	})
	sm.ForEach(func(item entity.StdPkgProp) {
		var mprop = &entity.PropDefine{
			Id:      item.Id,
			Name:    item.Name,
			Type:    item.Type,
			Vl:      item.Vl,
			Vll:     item.Vll,
			Vh:      item.Vh,
			Vhh:     item.Vhh,
			Scope:   "",
			Unit:    item.Unit,
			Monitor: item.Monitor,
		}
		if item.Vl != "" || item.Vh != "" {
			mprop.Scope = fmt.Sprintf("%v~%v", item.Vl, item.Vh)
		}
		mdev.Props = append(mdev.Props, *mprop)

		var mpropvalue = &entity.PropValue{
			Id:    item.Id,
			Fv:    item.Fv,
			Sv:    item.Sv,
			State: item.State,
			CrtDt: datetime.NowStr(),
		}
		if mpropvalue.Sv == "" {
			mpropvalue.Sv = strconv.FormatFloat(float64(mpropvalue.Fv), 'f', -1, 32)
		}
		mdev.PropValues = append(mdev.PropValues, *mpropvalue)
	})
	var _, err = GetDeviceCollection().InsertOne(context.TODO(), mdev)
	if err != nil {
		return err
	}
	*l = append(*l, mdev)
	return nil
}

func GetOldProp(l *[]entity.PropDefine, id string) *entity.PropDefine {
	for index, v := range *l {
		if v.Id == id {
			return &((*l)[index])
		}
	}
	return nil
}
func GetOldValue(l *[]entity.PropValue, id string) *entity.PropValue {
	for index, v := range *l {
		if v.Id == id {
			return &((*l)[index])
		}
	}
	return nil
}
func UpdateDevice(sid string, m *entity.StdPkgDevice, mdev *entity.StdPkgDeviceMongo) error {
	var bNeedSave = false
	var sm = stream.Of(m.Props...).Sorted(func(a, b entity.StdPkgProp) bool {
		return a.Id < b.Id
	})
	sm.ForEach(func(item entity.StdPkgProp) {
		var mprop = &entity.PropDefine{
			Id:      item.Id,
			Name:    item.Name,
			Type:    item.Type,
			Vl:      item.Vl,
			Vll:     item.Vll,
			Vh:      item.Vh,
			Vhh:     item.Vhh,
			Scope:   "",
			Unit:    item.Unit,
			Monitor: item.Monitor,
		}
		if item.Vl != "" || item.Vh != "" {
			mprop.Scope = fmt.Sprintf("%v~%v", item.Vl, item.Vh)
		}
		var moldprop = GetOldProp(&mdev.Props, mprop.Id)
		if moldprop == nil {
			mdev.Props = append(mdev.Props, *mprop)
			bNeedSave = true
		} else if mprop.Type != moldprop.Type ||
			mprop.Vl != moldprop.Vl ||
			mprop.Vll != moldprop.Vll ||
			mprop.Vh != moldprop.Vh ||
			mprop.Vhh != moldprop.Vhh ||
			mprop.Unit != moldprop.Unit ||
			mprop.Monitor != moldprop.Monitor {
			mprop.Type = moldprop.Type
			moldprop.Vl = mprop.Vl
			moldprop.Vll = mprop.Vll
			moldprop.Vh = mprop.Vh
			moldprop.Vhh = mprop.Vhh
			moldprop.Unit = mprop.Unit
			moldprop.Monitor = mprop.Monitor
			bNeedSave = true
		}

		var moldvalue = GetOldValue(&mdev.PropValues, mprop.Id)
		if moldvalue == nil {
			var mpropvalue = &entity.PropValue{
				Id:    item.Id,
				Fv:    item.Fv,
				Sv:    item.Sv,
				State: item.State,
				CrtDt: datetime.NowStr(),
			}
			if mpropvalue.Sv == "" {
				mpropvalue.Sv = strconv.FormatFloat(float64(mpropvalue.Fv), 'f', -1, 32)
			}
			mdev.PropValues = append(mdev.PropValues, *mpropvalue)

			bNeedSave = true
		}

	})

	if bNeedSave {
		var sm2 = stream.Of(mdev.Props...).Sorted(func(a, b entity.PropDefine) bool {
			return a.Id < b.Id
		})
		var defines []entity.PropDefine
		sm2.ForEach(func(item entity.PropDefine) {
			defines = append(defines, item)
		})
		mdev.Props = defines

		var updatefileds = bson.M{"props": mdev.Props,
			"propValues": mdev.PropValues}
		filter := bson.M{"_id": mdev.Id}
		update := bson.M{"$set": updatefileds}
		operation := mongo.NewUpdateOneModel()
		operation.SetFilter(filter)
		operation.SetUpdate(update)
		var sr = GetDeviceCollection().FindOneAndUpdate(context.TODO(), filter, update)
		return sr.Err()
	}
	return nil
}
